use std::{
    cell::Cell,
    collections::HashMap,
    fmt::format,
    process::Command,
    sync::{
        mpsc::{channel, Receiver, Sender},
        Arc,
    },
    thread,
};

use egui::Vec2;
use nalgebra::Point3;
use thunderdome::Index;
use QcCore::{
    ecs::{
        component::Component,
        components::{
            camera::Camera,
            light::{
                AmbientBoxLight, AmbientSphereLight, DirectionalLight, Light, LightType,
                PointLight, SpotLight,
            },
            material_render::MaterialRender,
            mesh_render::MeshRender,
            transform::Transform,
        },
        game_object::GameObject,
    },
    resources::material::Material,
    scene_system::scene::Scene,
};
use QcRender::resources::{Mesh, Shader, Texture, TextureKind, UniformInfo};
use QcScript::core::JsComponent;
use QcTools::utils::r#ref::Ref;
use QcUI::{
    component::{Button, Canvas, Grid, Label, Panel, TextBox, ToUi},
    core::uiBind::UiBind,
};
use QcWindowing::{
    dpi::LogicalSize, event::WindowEvent, event_loop::EventLoop, settings::WindowSettings,
    window::QcWindow,
};

use crate::{
    managers::page_manager::PageManager,
    pages::{EditorPage, ProjectHubPage},
    panel::LayerPanelMessage,
};

use super::{
    component_data::ComponentData,
    context::Context,
    editor_message_center::EditorMessageCenter,
    editor_renderer::EditorRenderer,
    message::{ComponentType, EditorMessage, GameObjectType, Page, UiType},
};

pub struct Editor {
    pub context: Arc<Context>,
    pub page_manager: PageManager,
    pub window: Ref<QcWindow>,
    pub editor_renderer: Ref<EditorRenderer>,
    receiver: Receiver<EditorMessage>,
    sender: Sender<EditorMessage>,
}

impl Editor {
    pub fn new(setting: WindowSettings, el: &EventLoop<()>) -> Self {
        let window = QcWindow::new(&el, setting);

        let context = Context::new(window.clone(), &el);

        let (sender, receiver) = channel();

        let editor_renderer = Ref::new(EditorRenderer::new(context.clone()));

        let project_hub_panel = Box::new(ProjectHubPage::new(sender.clone()));

        let mut page_manager = PageManager::new();

        page_manager.add_page(Page::ProjectHub, project_hub_panel);

        let message_center = Arc::new(EditorMessageCenter::new());

        Self {
            window,
            context,
            page_manager,
            editor_renderer,
            sender,
            receiver,
        }
    }

    pub fn pre_update(&self, event: &WindowEvent) {
        let window = self.context.window.try_read().unwrap();

        let result = self
            .context
            .ui_manager
            .try_write()
            .unwrap()
            .handleEvent(&window, event);

        if result.repaint {
            window.request_redraw();
            return;
        }
    }
    pub fn update(&mut self) {
        {
            let renderer = self.context.renderer.try_read().unwrap();
            renderer.setClearColor(0.66, 0.66, 0.66, 1.);
            renderer.clear(true, true, false);

            let mut uiManager = self.context.ui_manager.try_write().unwrap();

            let window = self.window.try_read().unwrap();

            if let Some(page) = self.page_manager.get_current_mut() {
                let mut canvas_list = vec![page.get_canvas()];
                uiManager.render_top(&window, &mut canvas_list);
                uiManager.update_not_js(canvas_list);

                page.update();
            }
        }

        while let Ok(msg) = self.receiver.try_recv() {
            match msg {
                EditorMessage::GoTo(page) => {
                    self.page_manager.navigate(page);
                    self.after_navigate();
                }
                EditorMessage::GoToEditor(path, name) => {
                    self.page_manager.remove_page(Page::Editor);
                    let editor_panel = Box::new(EditorPage::new(
                        self.context.clone(),
                        self.editor_renderer.clone(),
                        self.sender.clone(),
                        path,
                    ));
                    self.page_manager.add_page(Page::Editor, editor_panel);
                    self.page_manager.navigate(Page::Editor);
                    self.after_navigate();
                }
                EditorMessage::AddGameObject(obj) => {
                    let editor_actions = self.context.editor_actions.clone();

                    let mut scene_manager = self.context.scene_manager.try_write().unwrap();

                    let scene = scene_manager.get_current_scene_mut().unwrap();

                    let mut go = GameObject::new("GameObject");
                    let transform = Transform::new(Point3::new(0., 0., 0.));
                    go.addComponent(Component::Transform(transform));

                    match obj {
                        GameObjectType::None => {}
                        GameObjectType::Plane => {
                            go.set_name("Plane");
                            let mut mesh_render = MeshRender::new();
                            let mut model = Mesh::new("plane.mesh");
                            model.setMaterialIndex(0);

                            mesh_render.addModel(model.into());

                            let mut material_render = MaterialRender::new();
                            let mut material = Material::default();

                            material.addTexture(Texture::empty());
                            material_render.addMaterial(material);
                            go.addComponent(Component::MeshRender(mesh_render));
                            go.addComponent(Component::MaterialRender(material_render));
                        }
                        GameObjectType::Cube => {
                            go.set_name("Cube");
                            let mut mesh_render = MeshRender::new();
                            let mut model = Mesh::new("cube.mesh");
                            model.setMaterialIndex(0);

                            mesh_render.addModel(model.into());

                            let mut material_render = MaterialRender::new();
                            let mut material = Material::default();

                            material.addTexture(Texture::empty());
                            material_render.addMaterial(material);
                            go.addComponent(Component::MeshRender(mesh_render));
                            go.addComponent(Component::MaterialRender(material_render));
                        }
                        GameObjectType::Ball => {
                            go.set_name("Ball");
                            let mut mesh_render = MeshRender::new();
                            let mut model = Mesh::new("ball.mesh");
                            model.setMaterialIndex(0);

                            mesh_render.addModel(model.into());

                            let mut material_render = MaterialRender::new();
                            let mut material = Material::default();

                            material.addTexture(Texture::empty());
                            material_render.addMaterial(material);
                            go.addComponent(Component::MeshRender(mesh_render));
                            go.addComponent(Component::MaterialRender(material_render));
                        }
                        GameObjectType::Capsule => {
                            go.set_name("Capsule");
                            let mut mesh_render = MeshRender::new();
                            let mut model = Mesh::new("capsule.mesh");
                            model.setMaterialIndex(0);

                            mesh_render.addModel(model.into());

                            let mut material_render = MaterialRender::new();
                            let mut material = Material::default();

                            material.addTexture(Texture::empty());
                            material_render.addMaterial(material);
                            go.addComponent(Component::MeshRender(mesh_render));
                            go.addComponent(Component::MaterialRender(material_render));
                        }
                        GameObjectType::Monkey => {
                            go.set_name("Monkey");

                            let mut mesh_render = MeshRender::new();
                            let mut model = Mesh::new("monkey.mesh");
                            model.setMaterialIndex(0);

                            mesh_render.addModel(model.into());

                            let mut material_render = MaterialRender::new();
                            let mut material = Material::default();

                            material.addTexture(Texture::empty());
                            material_render.addMaterial(material);
                            go.addComponent(Component::MeshRender(mesh_render));
                            go.addComponent(Component::MaterialRender(material_render));
                        }

                        GameObjectType::Canvas => {
                            if scene.get_main_canvas().is_some() {
                                return;
                            }
                            go.set_name("Canvas");
                            go.addComponent(Component::Other(Box::new(Canvas::new())));
                        }
                    }

                    if let Some(target) = editor_actions.target.get() {
                        scene.add_child_with_parent(go, Some(target));
                    } else {
                        scene.add_child(go);
                    }
                }
                EditorMessage::AddUi(ui) => {
                    self.apply_add_ui(ui);
                }
                EditorMessage::AddLight(light) => {
                    let editor_actions = self.context.editor_actions.clone();

                    let mut scene_manager = self.context.scene_manager.try_write().unwrap();

                    let scene = scene_manager.get_current_scene_mut().unwrap();

                    let mut go = GameObject::new("GameObject");
                    let transform = Transform::new(Point3::new(0., 0., 0.));
                    go.addComponent(Component::Transform(transform));

                    match light {
                        LightType::Point => {
                            go.set_name("Point");
                            go.addComponent(Component::PointLight(PointLight::default()));
                        }
                        LightType::Directional => {
                            go.set_name("DirectionalLight");
                            go.addComponent(Component::DirectionalLight(
                                DirectionalLight::default(),
                            ));
                        }
                        LightType::Spot => {
                            go.set_name("SpotLight");
                            go.addComponent(Component::SpotLight(SpotLight::default()));
                        }
                        LightType::AmbientBox => {
                            go.set_name("AmbientBoxLight");
                            go.addComponent(Component::AmbientBoxLight(AmbientBoxLight::default()));
                        }
                        LightType::AmbientSphere => {
                            go.set_name("AmbientSphereLight");
                            go.addComponent(Component::AmbientSphereLight(
                                AmbientSphereLight::default(),
                            ));
                        }
                    }
                    if let Some(target) = editor_actions.target.get() {
                        scene.add_child_with_parent(go, Some(target));
                    } else {
                        scene.add_child(go);
                    }
                }
                EditorMessage::AddComponent(component_type) => {
                    let editor_actions = self.context.editor_actions.clone();

                    let mut scene_manager = self.context.scene_manager.try_write().unwrap();

                    let scene = scene_manager.get_current_scene_mut().unwrap();

                    if let Some(target) = editor_actions.target.get() {
                        let obj = &mut scene[target];

                        let comp = match component_type {
                            ComponentData::Js(comp) => {
                                let comp =
                                    JsComponent::new(&comp.name, None).with_script(&comp.script);
                                Component::Other(Box::new(comp))
                            }
                            ComponentData::Native(comp) => match comp {
                                ComponentType::Camera => Component::Camera(Camera::default()),
                                ComponentType::Light => Component::Light(Light::default()),
                                ComponentType::MaterialRender => {
                                    let material_render =
                                        MaterialRender::new().with_material(Material::default());

                                    Component::MaterialRender(material_render)
                                }
                                ComponentType::MeshRender => {
                                    let mesh_render = MeshRender::new().with_mesh(Mesh::cube());
                                    Component::MeshRender(mesh_render)
                                }
                                ComponentType::SkyBox => todo!(),
                                ComponentType::Transform => {
                                    Component::Transform(Transform::default())
                                }
                                ComponentType::JsComponent => todo!(),
                                ComponentType::Other => todo!(),
                                _ => {
                                    todo!()
                                }
                            },
                        };

                        obj.addComponent(comp);
                    }
                }
                EditorMessage::SetTexture(path) => {
                    let editor_actions = self.context.editor_actions.clone();

                    if let Some(target) = editor_actions.target.get() {
                        let mut scene_manager = self.context.scene_manager.try_write().unwrap();

                        let scene = scene_manager.get_current_scene_mut().unwrap();
                        let material_render = scene[target]
                            .getComponentMut::<MaterialRender>()
                            .expect("找不到 MaterialRender 组件");

                        let resource_manager = self.context.resource_manager.clone();

                        if let Some(texture) = resource_manager.get(&path) {
                            let material = material_render.get_material_mut(0);

                            // material.addTexture(Texture::from_u8_array(
                            //     &texture.file.data,

                            //     TextureKind::Rectangle {
                            //         width: 0,
                            //         height: 0,
                            //     },
                            // ))
                        }
                    }
                }
                EditorMessage::SetMaterial(path) => {
                    let editor_actions = self.context.editor_actions.clone();

                    if let Some(target) = editor_actions.target.get() {
                        let mut scene_manager = self.context.scene_manager.try_write().unwrap();

                        let scene = scene_manager.get_current_scene_mut().unwrap();
                        let material_render = scene[target]
                            .getComponentMut::<MaterialRender>()
                            .expect("找不到 MaterialRender 组件");

                        let resource_manager = self.context.resource_manager.clone();

                        if let Some(text) = resource_manager.get(&path) {
                            let text = String::from_utf8(text.file.data.to_vec())
                                .expect(&format!("{} 文件解析失败", path));
                            let material: Material =
                                ron::from_str(&text).expect(&format!("{} 文件解析失败", path));
                            let name = material.shader.name.clone();

                            let mut hashmap = HashMap::new();
                            let mut slot = 0;
                            for (name, uniformInfo) in material.getUniformList().iter() {
                                match uniformInfo {
                                    UniformInfo::Texture(texture) => {
                                        let name = texture.getName();
                                        if name.contains(".dds") {
                                            if let Some(texture) = resource_manager.get(name) {
                                                hashmap.insert(
                                                    format!("Texture{}", slot),
                                                    UniformInfo::Texture(Texture::from_u8_array(
                                                        &texture.file.data,
                                                        &name,
                                                        TextureKind::rect(),
                                                    )),
                                                );
                                                slot += 1;
                                            }
                                        }
                                    }
                                    _ => {}
                                }
                            }

                            let material = material
                                .with_shader(Shader::new(&name))
                                .with_name(&path)
                                .with_uniform_list(hashmap);

                            material_render.set_material(0, material);
                        }
                    }
                }
                EditorMessage::SetMesh(path) => {
                    let editor_actions = self.context.editor_actions.clone();

                    if let Some(target) = editor_actions.target.get() {
                        let mut scene_manager = self.context.scene_manager.try_write().unwrap();

                        let scene = scene_manager.get_current_scene_mut().unwrap();
                        let mesh_render = scene[target]
                            .getComponentMut::<MeshRender>()
                            .expect("找不到 MeshRender 组件");

                        // 属于引擎自带的网格数据
                        if Mesh::default_meshs().contains(&path) {
                            mesh_render.set_mesh(0, Mesh::new(&path));
                            return;
                        }

                        let resource_manager = self.context.resource_manager.clone();

                        if let Some(text) = resource_manager.get(&path) {
                            let text = String::from_utf8(text.file.data.to_vec())
                                .expect(&format!("{} 文件解析失败", path));

                            let mesh = Mesh::loadByMeshFile(&text)
                                .expect(&format!("{} 文件解析失败", path));
                            mesh_render.set_mesh(0, mesh);
                        }
                    }
                }
                EditorMessage::RemoveGameObject(target) => {
                    self.apply_remove_game_object(target);
                }
                EditorMessage::RemoveUiNode(index) => {
                    self.apply_remove_ui_node(index);
                }
                EditorMessage::PlayGame(scene) => {
                    self.apply_play_game(scene);
                }
                EditorMessage::OpenScene(file) => {
                    let scene = std::fs::read_to_string(file.path);

                    if let Ok(scene) = scene {
                        let scene = Scene::load(&scene, self.context.resource_manager.clone());

                        let mut scene_manager = self.context.scene_manager.try_write().unwrap();

                        scene_manager.load_scene(scene);
                    }
                }

                EditorMessage::SaveScene => self.apply_save_scene(),
                EditorMessage::AddUiBind(uuid, bind) => {
                    let mut scene_manager = self.context.scene_manager.try_write().unwrap();
                    println!("{:?}", bind);
                    let scene = scene_manager.get_current_scene_mut().unwrap();

                    if let Some(index) = scene.get_main_canvas() {
                        if let Some(canvas) = scene[index].getComponentMut::<Canvas>() {
                            canvas.addUiBind(uuid, UiBind::JsBind(bind));
                        }
                    }
                }
            }
        }
    }

    pub fn post_update(&self) {
        self.context.device.swapBuffers();
    }

    fn after_navigate(&self) {
        let page = self.page_manager.get_current().unwrap();
        let window = self.window.try_read().unwrap();
        let size: Vec2 = page.get_size();
        if size == Vec2::INFINITY {
            window.set_maximized(true);
        } else {
            window.set_maximized(false);
            let _ = window.request_inner_size(LogicalSize::new(size.x, size.y));
        }
    }

    pub fn apply_remove_game_object(&self, target: Index) {
        let mut scene_manager = self.context.scene_manager.try_write().unwrap();

        let message_center = self.context.message_center.try_read().unwrap();
        message_center.send_to_layer_panel(LayerPanelMessage::RebuildList);

        let scene = scene_manager.get_current_scene_mut().unwrap();
        scene.remove_child(target);
        scene.remove(target);

        let editor_actions = self.context.editor_actions.clone();
        editor_actions.unselect();
    }

    pub fn apply_remove_ui_node(&self, target: Index) {
        let mut scene_manager = self.context.scene_manager.try_write().unwrap();

        let message_center = self.context.message_center.try_read().unwrap();
        message_center.send_to_layer_panel(LayerPanelMessage::RebuildList);

        let scene = scene_manager.get_current_scene_mut().unwrap();

        if let Some(index) = scene.get_main_canvas() {
            if let Some(canvas) = scene[index].getComponentMut::<Canvas>() {
                canvas.remove_child(target);
                canvas.remove(target);
            }
        }

        let editor_actions = self.context.editor_actions.clone();

        editor_actions.unselect();
    }

    pub fn apply_play_game(&self, scene: String) {
        let project_config = self.context.project_config.try_read().unwrap();
        project_config.as_ref().map(|config| {
            let path = config.get_path().clone();
            thread::spawn(move || {
                let output = Command::new("cmd")
                    .arg(format!("/C cd {} && npm run build", path.display()))
                    .output()
                    .unwrap();
            });

            let path = config.get_path().join("build/windows");
            thread::spawn(move || {
                println!(
                    "{}",
                    format!("/C cd {} && cargo run -- {:?}", path.display(), scene)
                );
                let output = Command::new("cmd")
                    .arg(format!(
                        "/C cd {} && cargo run -- {:?}",
                        path.display(),
                        scene
                    ))
                    .output()
                    .unwrap();
            });
        });
    }

    pub fn apply_save_scene(&self) {
        let scene_manager = self.context.scene_manager.try_read().unwrap();

        let scene = scene_manager
            .get_current_scene()
            .expect("无法获取当前的场景对象");

        let scene = ron::to_string(scene).unwrap();
        let config = self.context.project_config.try_read().unwrap();
        let config = config.as_ref().unwrap();

        let path = config.get_path().join("assets").join(&config.scene);

        std::fs::write(path, scene).unwrap();
    }

    fn apply_add_ui(&self, ui: UiType) {
        let editor_actions = self.context.editor_actions.clone();

        let mut scene_manager = self.context.scene_manager.try_write().unwrap();

        let scene = scene_manager.get_current_scene_mut().unwrap();

        if let Some(index) = scene.get_main_canvas() {
            let obj = &mut scene[index];

            let node;
            if let Some(canvas) = obj.getComponentMut::<Canvas>() {
                match ui {
                    UiType::Button => {
                        node = Button::default().toUi();
                    }
                    UiType::Label => {
                        node = Label::default().with_selectable(false).toUi();
                    }
                    UiType::TextBox => {
                        node = TextBox::default().toUi();
                    }
                    UiType::Panel => {
                        node = Panel::default().toUi();
                    }
                    UiType::Grid => {
                        node = Grid::default().toUi();
                    }
                    UiType::RadioBox => {
                        todo!()
                    }
                    UiType::CheckBox => todo!(),
                }
                if let Some(parent) = editor_actions.get_current_ui_node() {
                    canvas.add_child_with_parent(node, Some(parent));
                } else {
                    canvas.add_child(node);
                }
            }
        }
    }
}
